package org.moonlight.jvm.instructions.references;

import org.moonlight.jvm.instructions.base.Index16Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.OperandStack;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.Slots;
import org.moonlight.jvm.rtda.share.heap.classmember.Field;
import org.moonlight.jvm.rtda.share.heap.symref.FieldRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

/**
 * GetField: 获取对象的实例变量值，然后推入操作数栈。
 *           它需要两个操作数:
 *             1. 常量池索引 - 来自操作数，一个 uint16 索引
 *             2. 对象引用 - 从操作数栈弹出
 *
 * @author Moonlight
 * @createTime 2023/8/30 15:32
 **/
public class GetField extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 当前运行时常量池
        RtConstantPool rtCp = frame.getMethod().getClazz().getRtCp();
        // 根据 字段符号引用索引值 获取 字段符号引用
        FieldRef fieldRef = (FieldRef) rtCp.getConstant(this.idx);
        // 根据符号引用解析出对应的字段
        Field field = fieldRef.resolvedField();
        // 解析后的字段必须是实例字段，否则抛出IncompatibleClassChangeError
        if (field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }
        // 操作数栈
        OperandStack operandStack = frame.getOperandStack();
        Object ref = operandStack.popRef();
        if (ref == null) {
            throw new NullPointerException();
        }

        String descriptor = field.getDescriptor();
        int slotId = field.getSlotId();
        Slots fields = ref.getFields();

        switch (descriptor.substring(0, 1)) {
            case "Z":
            case "B":
            case "C":
            case "S":
            case "I":
                operandStack.pushInt(fields.getInt(slotId));
                break;
            case "F":
                operandStack.pushFloat(fields.getFloat(slotId));
                break;
            case "J":
                operandStack.pushLong(fields.getLong(slotId));
                break;
            case "D":
                operandStack.pushDouble(fields.getDouble(slotId));
                break;
            case "L":
            case "[":
                operandStack.pushRef(fields.getRef(slotId));
                break;
            default:
                throw new RuntimeException("不支持的描述符: " + descriptor);
        }
    }

}
